package monitor

import (
	"github.com/astaxie/beego/logs"
	"fmt"
	"github.com/astaxie/beego/orm"
	"time"
	"os"
	"path/filepath"
	"cloud_worm/util/uhttp"
	"cloud_worm/util/ufile"
	"cloud_worm/util/proto/ujson"
	"cloud_worm/util/umodels/mysqldb"
	"cloud_worm/util/umodels/mysqldb/dbmanager"
	"cloud_worm/util/umodels/mysqldb/dbmonitor"
	"cloud_worm/wormweb/controllers/ctrconfig"
	"cloud_worm/wormweb/controllers/ctrutil"
	"cloud_worm/wormweb/controllers/ctrinner/taskqueue"
)

type CmdWhiteController struct {
	ctrconfig.LoginCtr
}

// CmdWhiteCtr.List get list
func (c *CmdWhiteController) List() {
	c.Manager()
	offset, limit := c.DealPage()

	status, err := c.GetInt("status", mysqldb.StatusAll)
	key := c.GetString("key")
	typ, _ := c.GetInt("type", mysqldb.SelectIntAll)

	w := &dbmonitor.CmdWhite{}
	count, err := w.Count(status, key, typ)
	if err != nil {
		logs.Warn(fmt.Sprintf("CmdWhite count error: %s", err.Error()))
		data := map[string]interface{}{"total": 0, "data": []dbmonitor.CmdWhite{}}
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "count error.", data)
		c.ServeJSON()
		return
	}
	if count == 0 {
		data := map[string]interface{}{"total": count, "data": []dbmonitor.CmdWhite{}}
		c.Data["json"] = uhttp.CreateOkResponse(data)
		c.ServeJSON()
		return
	}

	l, err := w.Select(offset, limit, status, key, typ)
	if err != nil {
		logs.Warn(fmt.Sprintf("CmdWhite select error: %s", err.Error()))
		data := map[string]interface{}{"total": 0, "data": []dbmonitor.CmdWhite{}}
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "select error.", data)
		c.ServeJSON()
		return
	}

	data := map[string]interface{}{"total": count, "data": l}
	c.Data["json"] = uhttp.CreateOkResponse(data)
	c.ServeJSON()
	return
}

// CmdWhiteCtr.Create add one record
func (c *CmdWhiteController) Create()  {
	c.Manager()

	type InputInfo struct {
		Key 		string		`json:"key"`
		Type 		int 		`json:"type"`
	}
	var input InputInfo
	if err := c.Unmarshal(&input); err != nil {
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeReqBodyErr, err.Error(), nil)
		c.ServeJSON()
		return
	}
	if input.Key == mysqldb.SelectStrAll || (input.Type != dbmonitor.WhiteKeyTypeParent && input.Type != dbmonitor.WhiteKeyTypeSelf) {
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "input params error.", nil)
		c.ServeJSON()
		return
	}

	w := &dbmonitor.CmdWhite{WhiteKey: input.Key, Type: input.Type}
	if err := w.Read("white_key", "type"); err == nil {
		logs.Error(fmt.Sprintf("had key: %s", input.Key))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "key is had.", nil)
		c.ServeJSON()
		return
	}
	w.Created = time.Now().Unix()
	w.Status = mysqldb.StatusValid
	w.Updated = time.Now().Unix()
	if err := w.Insert(); err != nil {
		logs.Error(fmt.Sprintf("CmdWhite insert error: %s", err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "insert error.", nil)
		c.ServeJSON()
		return
	}

	fd, err := os.OpenFile(cmdRuleFilePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		logs.Error(fmt.Sprintf("CmdWhite create file error: %s", err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "create file error.", nil)
		c.ServeJSON()
		return
	}
	defer fd.Close()

	c.Data["json"] = uhttp.CreateOkResponse(nil)
	c.ServeJSON()
	return
}

// CmdWhiteCtr.Edit edit one record
func (c *CmdWhiteController) Edit()  {
	c.Manager()

	type InputInfo struct {
		Id 			int64		`json:"id"`
		Key 		string		`json:"key"`
		Type 		int 		`json:"type"`
		Status 		int 		`json:"status"`
	}
	var input InputInfo
	if err := c.Unmarshal(&input); err != nil {
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeReqBodyErr, err.Error(), nil)
		c.ServeJSON()
		return
	}
	if input.Key == mysqldb.SelectStrAll || (input.Type != dbmonitor.WhiteKeyTypeParent && input.Type != dbmonitor.WhiteKeyTypeSelf) {
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "input params error.", nil)
		c.ServeJSON()
		return
	}

	w := &dbmonitor.CmdWhite{Id: input.Id}
	if err := w.Read("id"); err != nil {
		logs.Warn(fmt.Sprintf("CmdWhite edit input id error: %s.", err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "input id error.", nil)
		c.ServeJSON()
		return
	}

	w.Updated = time.Now().Unix()
	w.WhiteKey = input.Key
	w.Type = input.Type
	w.Status = input.Status
	if err := w.Update("status", "white_key", "updated", "type"); err != nil {
		logs.Warn(fmt.Sprintf("CmdWhite updated error: %s.", err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "updated error.", nil)
		c.ServeJSON()
		return
	}

	fd, err := os.OpenFile(cmdRuleFilePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		logs.Error(fmt.Sprintf("CmdWhite create file error: %s", err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "create file error.", nil)
		c.ServeJSON()
		return
	}
	defer fd.Close()

	c.Data["json"] = uhttp.CreateOkResponse(nil)
	c.ServeJSON()
	return
}

// CmdWhiteController.CommitFlag 标记是否需要commit处理
// cmd_white_key_file 文件存在表明是待commit
// 因为commit时，会将cmd_white_key_file rename为cmd_white_key_file.bak，所以一旦commit过，cmd_white_key_file文件就不会存在
func (c *CmdWhiteController) CommitFlag() {
	c.Manager()

	commit := false
	_, err := os.Stat(cmdRuleFilePath)
	if err != nil {
		commit = false
	} else {
		commit = true
	}

	c.Data["json"] = uhttp.CreateOkResponse(map[string]interface{}{"commit": commit})
	c.ServeJSON()
	return
}

// CmdWhiteCtr.Commit 确认提交
func (c *CmdWhiteController) Commit() {
	c.Manager()

	// 生成配置文件
	cmdWhite := &dbmonitor.CmdWhite{}
	cmdWhites, err := cmdWhite.Select(mysqldb.SelectOffsetZero, mysqldb.SelectLimitAll, mysqldb.StatusValid,
		mysqldb.SelectStrAll, mysqldb.SelectIntAll)
	if err != nil {
		logs.Error(fmt.Sprintf("select all cmdwhitekey error: %s", err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "select all cmdwhite key error", nil)
		c.ServeJSON()
		return
	}
	fd, err := os.OpenFile(cmdRuleFilePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		logs.Error(fmt.Sprintf("open file'%s' error: %s", cmdRuleFilePath, err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, fmt.Sprintf("open file'%s' error", cmdRuleFilePath), nil)
		c.ServeJSON()
		return
	}
	for _, item := range cmdWhites {
		fd.WriteString(fmt.Sprintf("%d|%s\n", item.Type, item.WhiteKey))
	}
	fd.Close()

	newMd5 := ufile.GetFileMd5(cmdRuleFilePath)
	oldMd5 := ufile.GetFileMd5(cmdRuleFileBakPath)
	if newMd5 == oldMd5 {
		os.Rename(cmdRuleFilePath, cmdRuleFileBakPath)
		c.Data["json"] = uhttp.CreateOkResponse(nil)
		c.ServeJSON()
		return
	}

	downloadURL, err := ctrutil.ResourceSet().UploadFile(cmdRuleFilePath, ctrutil.UploadWormURL)
	if err != nil {
		logs.Error(fmt.Sprintf("upload file to router error: %s", err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "upload file to router error", nil)
		c.ServeJSON()
		return
	}
	os.Rename(cmdRuleFilePath, cmdRuleFileBakPath)

	// 如果没有启动命令监控的功能，则仅将配置文件提交
	if CmdMonitor == false {
		logs.Debug("cmd monitor=%v, commit config file", CmdMonitor)
		c.Data["json"] = uhttp.CreateOkResponse(nil)
		c.ServeJSON()
		return
	}

	// 选择目标
	wormId := dbmanager.WormId{}
	wormIds, err := wormId.SelectStatusVerOver(mysqldb.SelectOffsetZero, mysqldb.SelectLimitAll, dbmanager.AgentOnline,
		CmdMonitorConfigVersion, ctrutil.RunMode)
	if err != nil && err != orm.ErrNoRows {
		logs.Error(fmt.Sprintf("CmdWhiteCtr get online agent failed: %s", err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "CmdWhiteCtr get online agent failed.", nil)
		c.ServeJSON()
		return
	}

	// 转发出去
	confPayLoad := ujson.ConfigFilePayLoad{
		FileURL: downloadURL,
		MD5: newMd5,
	}
	payLoad, err := confPayLoad.MarshalJSON()
	if err != nil {
		logs.Error(fmt.Sprintf("cmd ConfigFilePayLoad marshal json failed: %s", err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "ConfigFilePayLoad marshal json failed.", nil)
		c.ServeJSON()
		return
	}
	for _, item := range wormIds {
		notifyItem := &NotifyAutoConfig{
			AccessSrv: item.ServerAddr,
			AgentID: item.AgentId,
			AgentIP: item.IP,
			NotifyType: ujson.NtCmdRule,
			PayLoad: payLoad,
		}
		taskqueue.FastQueInst().MsgQ.Push(notifyItem)
	}

	c.Data["json"] = uhttp.CreateOkResponse(nil)
	c.ServeJSON()
	return
}

func (c *CmdWhiteController) EnableSet()  {
	c.Manager()

	type InputInfo struct {
		Enable 		bool		`json:"enable"`
	}
	var input InputInfo
	if err := c.Unmarshal(&input); err != nil {
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeReqBodyErr, err.Error(), nil)
		c.ServeJSON()
		return
	}

	if input.Enable == CmdMonitor {
		logs.Debug("cmd monitor enable had: ", CmdMonitor)
		c.Data["json"] = uhttp.CreateOkResponse(nil)
		c.ServeJSON()
		return
	}
	CmdMonitor = input.Enable
	cmdMonitorUpdate()

	// 选择目标
	wormId := dbmanager.WormId{}
	wormIds, err := wormId.SelectStatusVerOver(mysqldb.SelectOffsetZero, mysqldb.SelectLimitAll, dbmanager.AgentOnline,
		CmdMonitorConfigVersion, ctrutil.RunMode)
	if err != nil && err != orm.ErrNoRows {
		logs.Error(fmt.Sprintf("cmd monitor enable get online agent failed: %s", err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "cmd monitor enable get online agent failed.", nil)
		c.ServeJSON()
		return
	}

	// 转发出去
	enablePayLoad := ujson.EnablePayLoad{
		Enable: CmdMonitor,
	}
	payLoad, err := enablePayLoad.MarshalJSON()
	if err != nil {
		logs.Error(fmt.Sprintf("cmd EnablePayLoad marshal json failed: %s", err.Error()))
		c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "EnablePayLoad marshal json failed.", nil)
		c.ServeJSON()
		return
	}
	for _, item := range wormIds {
		notifyItem := &NotifyAutoConfig{
			AccessSrv: item.ServerAddr,
			AgentID: item.AgentId,
			AgentIP: item.IP,
			NotifyType: ujson.NtCmdMonitor,
			PayLoad: payLoad,
		}
		taskqueue.FastQueInst().MsgQ.Push(notifyItem)
	}

	c.Data["json"] = uhttp.CreateOkResponse(nil)
	c.ServeJSON()
	return
}

func (c *CmdWhiteController) EnableGet()  {
	c.Manager()

	c.Data["json"] = uhttp.CreateOkResponse(map[string]interface{}{"enable": CmdMonitor})
	c.ServeJSON()
	return
}

func cmdMonitorUpdate() {
	conf := &dbmanager.WebConfig{Name: CmdMonitorEnableName}
	if err := conf.Read("name"); err == nil {
		conf.Value = fmt.Sprintf("%v", CmdMonitor)
		conf.Updated = time.Now().Unix()
		conf.Update("value", "updated")
	} else {
		logs.Error(fmt.Sprintf("no '%s' config record", CmdMonitorEnableName))
	}
	return
}

var CmdMonitor bool

const CmdMonitorEnableName = "cmd_monitor_enable"

func CmdMonitorLoad() error {
	conf := &dbmanager.WebConfig{Name: CmdMonitorEnableName}
	if err := conf.Read("name"); err == nil {
		switch conf.Value {
		case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "Y", "y", "ON", "on", "On":
			CmdMonitor = true
		case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "N", "n", "OFF", "off", "Off":
			CmdMonitor = false
		default:
			return fmt.Errorf("config value format error")
		}
	} else {
		return fmt.Errorf("读WolConfig(%s)错误: %s", CmdMonitorEnableName, err.Error())
	}

	logs.Info(fmt.Sprintf("set %s=%v", CmdMonitorEnableName, CmdMonitor))

	return nil
}

func init()  {
	ctrconfig.RegisterConfigLoad(CmdMonitorEnableName, CmdMonitorLoad)

	// 尝试创建该列数据
	conf := &dbmanager.WebConfig{Name: CmdMonitorEnableName}
	if err := conf.Read("name"); err != nil {
		conf.Commit = "文件监控开关"
		conf.Type = dbmanager.BoolValue
		conf.Value = "false"
		conf.Created = time.Now().Unix()
		conf.Updated = conf.Created
		conf.Status = mysqldb.StatusValid
		conf.Insert()
	}

	CmdMonitorLoad()
}

var cmdRuleFilePath string
var cmdRuleFileBakPath string
func init()  {
	cmdRuleFilePath = filepath.Join(ctrutil.TmpDir, ujson.CmdRuleFile)
	cmdRuleFileBakPath = filepath.Join(ctrutil.TmpDir, ujson.CmdRuleFile + ".bak")
}